package org.jugile.util;

import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class JExpr extends Jugile {

	private JExpr next;
	private List<JExpr> params = new ArrayList<JExpr>();
	
	private String mname = null;
	private Method m = null;
	private Class clazz = null;
	private boolean isConst = false;
	
	public Object eval(List<Object> cstack) {
		Object[] args = new Object[params.size()];
		int idx = 0;
		for (JExpr p : params) {
			args[idx++] = p.eval(cstack);
		}
		try {
			return m.invoke(getObj(cstack), args);
		} catch (Exception e) {
			fail(e); return null;
		}
	}
	
	private Object getObj(List<Object> cstack) {
		if (mname.startsWith("/")) return cstack.get(0); // root
		String name = mname;
		int prev = 0;
		while (name.startsWith("../")) {
			name = name.substring("../".length());
			prev++;
		}
		return cstack.get(cstack.size()-1-prev);
	}

	private boolean isRoot = false;
	private int parentCount = 0;
	private enum State {START,PARAMS};
	private State state = State.START;
	public void parse(PullReader pr) {
		while (pr.hasNext()) {
			char ch = pr.next();
			print("ch: " + ch);
			if (state == State.START) {
				if (ch == '/') {
					isRoot = true;
					continue;
				}
				if (ch == '.') {
					if (pr.match("./")) {
						parentCount++;
						continue;
					} 
					next = new JExpr();
					next.parse(pr);
					continue;
				}
				if (ch == '(') {
					if (mname == null) fail("parse error: has '(' without mname");
					state = State.PARAMS;
					JExpr p = new JExpr();
					p.parse(pr);
					params.add(p);
					continue;
				}
				if (ch == ',') {
					pr.push(ch);
					return;
				}
				if (ch == ')') {
					pr.push(ch);
					return;
				}
			}
			if (state == State.PARAMS) {
				if (ch == ',') {
					state = State.START;
					pr.push('('); // read next param
					continue;
				}
				if (ch == ')') {
					state = State.START;
					continue;
				}
				continue;
			}
			mname = ""+ch+pr.readIdentifier();
		}
	}
	
	public void parse(String str) {
		parse(new PullReader(str));
	}

	
	public void setConst(String v) {
		mname = v;
		isConst = true;
	}

	public String toString() {
		Buffer buf = new Buffer();
		dump(buf);
		return buf.toString();
	}
	
	private void dump(Buffer buf) {
		if (isRoot) buf.add("/");
		if (parentCount > 0) {
			for (int i = 0; i < parentCount; i++) buf.add("../");
		}
		buf.add(mname);
		if (params.size() > 0) {
			buf.add("(");
			for (int i = 0; i < params.size(); i++) {
				if (i > 0) buf.add(",");
				params.get(i).dump(buf);
			}
			buf.add(")");
		}
		if (next != null) {
			buf.add(".");
			next.dump(buf);
		}
	}
}
